# coding=utf-8
class Block(object):
    def __init__(self, _type=0, target=0, level=0):
        self.type = _type
        self.target = target
        self.level = level


class CO_FLAGS(object):
    CO_VARARGS = 0x4
    CO_VARKEYWORDS = 0x8
    CO_GENERATOR = 0x20


class FrameObject:
    def __init__(self, codes=None, func=None, args=None, op_arg=None):
        self.locals = {}
        self.fast_locals = None

        if func:
            self.globals = func.globals
            codes = func.func_code
            if func.defaults:
                dft_cnt = len(func.defaults)
                arg_cnt = codes.arg_count

                self.fast_locals = [None for _ in range(arg_cnt)]

                while dft_cnt > 0:
                    dft_cnt -= 1
                    arg_cnt -= 1
                    self.fast_locals[arg_cnt] = func.defaults[dft_cnt]
        else:
            self.globals = self.locals
            self.locals["__name__"] = "__main__"

        alist = None  # 扩展位置参数 *args
        adict = None  # 扩展键参数 **kwargs

        if args:
            arg_cnt = codes.arg_count
            na = op_arg & 0xFF
            nk = op_arg >> 8
            if self.fast_locals is None:
                self.fast_locals = [None for _ in range(arg_cnt)]

            if arg_cnt < na:
                for i in range(arg_cnt):
                    self.fast_locals[i] = args[i]
                alist = []
                for i in range(arg_cnt, na):
                    alist.append(args[i])
            else:
                for i in range(na):
                    self.fast_locals[i] = args[i]

            for i in range(nk):
                key = args[na + i * 2]
                value = args[na + i * 2 + 1]
                if key in codes.var_names:
                    index = codes.var_names.index(key)
                    self.fast_locals[index] = value
                else:
                    if adict is None:
                        adict = dict()

                    adict[key] = value

        if codes.flag & CO_FLAGS.CO_VARARGS:
            if alist is None:
                alist = []
            self.fast_locals.append(alist)

        if codes.flag & CO_FLAGS.CO_VARKEYWORDS:
            if adict is None:
                adict = dict()
            self.fast_locals.append(adict)

        self.stack = []
        self.loop_stack = []

        self.consts = codes.consts
        self.names = codes.names

        self.pc = 0

        self._code_object = codes

        self.sender = None

        self.is_entry_frame = False

    def has_more_codes(self):
        return self.pc < len(self._code_object.byte_codes)

    def get_op_code(self):
        op_code = ord(self._code_object.byte_codes[self.pc])
        self.pc += 1
        return op_code

    def get_op_arg(self):
        byte1 = ord(self._code_object.byte_codes[self.pc]) & 0xFF
        self.pc += 1
        byte2 = ord(self._code_object.byte_codes[self.pc]) & 0xFF
        self.pc += 1
        op_arg = byte2 << 8 | byte1
        return op_arg

    def push(self, item):
        self.stack.append(item)

    def pop(self):
        return self.stack.pop()

    def top(self):
        return self.stack[-1]

    def get_name(self, index):
        return self.names[index]

    def get_local(self, key):
        return self.locals.get(key, None)

    def set_local(self, key, value):
        self.locals[key] = value

    def stack_level(self):
        return len(self.stack)

    def is_first_frame(self):
        return self.sender is None
